//
//  ViewController.m
//  LockTest
//
//  Created by sank zhang on 2019/1/15.
//  Copyright © 2019年 sank zhang. All rights reserved.
//

//可以看到除了 OSSpinLock 外，dispatch_semaphore 和 pthread_mutex 性能是最高的。苹果在新系统中已经优化了 pthread_mutex 的性能，所以它看上去和 OSSpinLock 差距并没有那么大了。
//
//可以看到YYKit组件中YYCache 和 YYImageCoder大量使用 dispatch_semaphore  pthread_mutex这两个锁
//
//OSSpinLock 自旋锁(虽然已经被证明不安全 优先级翻转)，性能最高的锁。原理很简单，就是一直 do while 忙等。它的缺点是当等待时会消耗大量 CPU 资源，所以它不适用于较长时间的任务。对于内存缓存的存取来说，它非常合适。
//
//dispatch_semaphore 是信号量，但当信号总量设为 1 时也可以当作锁来。在没有等待情况出现时，它的性能比 pthread_mutex 还要高，但一旦有等待情况出现时，性能就会下降许多。相对于 OSSpinLock 来说，它的优势在于等待时不会消耗 CPU 资源。对磁盘缓存来说，它比较合适。
//
//不存在等待的情况，例如不涉及到磁盘这种文件读写，dispatch_semaphore性能很高，如果涉及到的任务等待时间较长，就需要用pthread_mutex(OSSpinLock不安全就可以先不用了)
//
//按计算机网络书上的介绍，mutex是信号量的简化版本，可以简单判定，pthread_mutex可能是用信号量来实现的，内部也会用在进入临界区之前根据条件进行休眠。这就可以避免优先级翻转的问题，在获取锁失败的时候，调用thread_yield将CPU主动放弃给另一个线程，这样就没有忙等待，下一次时间片轮转到的时候再进行锁的测试，YY那里有个测试表，信号量性能略微高于pthread_mutex，可能是因为pthread_mutex类型转换的问题，功能更丰富，还有递归锁，上层封装的转换可能就性能稍微差一点。


#import "ViewController.h"
#import <pthread.h>

@interface ViewController (){
//    pthread_mutex_t _mutexlock;
    dispatch_semaphore_t _semaphore;
}
@property (nonatomic, assign) pthread_mutex_t mutexlock;
//@property (nonatomic, weak) dispatch_semaphore_t semaphore;
@property (nonatomic, strong) NSLock *lock;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)textSemaphore
{
    //当信号总量设为 1 时也可以当作锁来
    _semaphore = dispatch_semaphore_create(1);
    dispatch_semaphore_signal(_semaphore);
    dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
}

//互斥锁 同一线程多次 lock 是不允许的
- (void)testMutex
{
    //初始化
    pthread_mutex_init(&_mutexlock, NULL);
    //加锁
    pthread_mutex_lock(&_mutexlock);
    //解锁
    pthread_mutex_unlock(&_mutexlock);
    //释放
    pthread_mutex_destroy(&_mutexlock);
    //可以用来判断是否加锁
    //加锁时返回的是 0，否则返回一个错误提示码；后者返回的 YES和NO
    pthread_mutex_trylock(&_mutexlock);
    if (pthread_mutex_trylock(&_mutexlock) == 0) {
        //.........
        pthread_mutex_unlock(&_mutexlock);
    }
}

//
//下面的代码如果我们用 pthread_mutex_init(&pLock, NULL) 初始化会出现死锁的情况，递归锁能很好的避免这种情况的死锁
- (void)testMutexAtt
{
    static pthread_mutex_t pLock;
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr); //初始化attr并且给它赋予默认
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); //设置锁类型，这边是设置为递归锁
    pthread_mutex_init(&pLock, &attr);
    pthread_mutexattr_destroy(&attr); //销毁一个属性对象，在重新进行初始化之前该结构不能重新使用
    
    //1.线程1 递归锁NSRecursiveLock
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        static void (^RecursiveBlock)(int);
        RecursiveBlock = ^(int value) {
            pthread_mutex_lock(&pLock);
            if (value > 0) {
                NSLog(@"value: %d", value);
                RecursiveBlock(value - 1);
            }
            pthread_mutex_unlock(&pLock);
        };
        RecursiveBlock(5);
    });
}

//NSLock 普通锁
- (void)testLock
{
//    trylock：能加锁返回 YES 并执行加锁操作，相当于 lock，反之返回 NO
    [self.lock tryLock];
//    lockBeforeDate：这个方法表示会在传入的时间内尝试加锁，若能加锁则执行加锁操作并返回 YES，反之返回 NO
    //线程1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程1 尝试加速ing...");
        [self.lock lock];
        sleep(3);//睡眠5秒
        NSLog(@"线程1");
        [self.lock unlock];
        NSLog(@"线程1解锁成功");
    });
    
    //线程2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程2 尝试加速ing...");
        BOOL x =  [self.lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:4]];
        if (x) {
            NSLog(@"线程2");
            [self.lock unlock];
        }else{
            NSLog(@"失败");
        }
    });
}

//NSRecursiveLock 递归锁
//递归锁可以被同一线程多次请求，而不会引起死锁。这主要是用在循环或递归操作中。
- (void)testRecursiveLock
{
    NSLock *rLock = [NSLock new];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        static void (^RecursiveBlock)(int);
        RecursiveBlock = ^(int value) {
            [rLock lock];
            if (value > 0) {
                NSLog(@"线程%d", value);
                RecursiveBlock(value - 1);
            }
            [rLock unlock];
        };
        RecursiveBlock(4);
    });
}

//@synchronized 条件锁
- (void)testSynchronized
{
    //线程1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        @synchronized (self) {
            sleep(2);
            NSLog(@"线程1");
        }
    });
    
    //线程2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        @synchronized (self) {
            NSLog(@"线程2");
        }
    });
}

//当信号总量设为 0 永久等待信号,可以用来等待线程，一旦有等待情况出现时，性能就会下降许多
//传入值必须 >=0, 若传入为 0 则阻塞线程并等待timeout,时间到后会执行其后的语句
- (void)textSemaphore2{
    dispatch_queue_t queque = dispatch_queue_create("GoyakodCreated", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queque, ^{
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        [self getToken:semaphore];
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        [self request];
    });
}

- (void)getToken:(dispatch_semaphore_t)semaphore
{
    [NSThread sleepForTimeInterval:2.0];
    NSLog(@"-----getToken-------");
    
    //发送信号量:
    dispatch_semaphore_signal(semaphore);
}

- (void)request
{
    NSLog(@"-----request-------");
}

- (NSLock *)lock
{
    if (_lock == nil) {
        _lock = [[NSLock alloc] init];
    }
    return _lock;
}
@end
